package goActorsNaming

import(
	"os"
	"KademliaDht"
	"goActorsCommon"
	"gob"
	"rpc"
	"net"
	"bytes"
	"strconv"
)

type GoActorsNaming struct {	
	Dht	*KademliaDht.KademliaDht
	Pid	*GoActorsNamingPid
}

func New_GoActorsNaming( ip string, bootstrap *KademliaDht.KadContact ) ( gn *GoActorsNaming, error os.Error ) {
	goActorsCommon.RegisterTypesGob()
	
	dht, error := KademliaDht.New_KademliaDht( ip, 20, 5, bootstrap )
	if error != nil {
		return nil, error
	}
	
	gn = &GoActorsNaming{ dht, nil }
	
	// Starts RPC Service
	server := rpc.NewServer()
	server.Register ( gn )
	l, e := net.ListenTCP ( "tcp", &net.TCPAddr{ make([]byte, 0, 0), 0 } )
	if ( e != nil ) {
		return nil, e
	}
	
	go server.Accept( l )
	
	addr, _ := l.Addr().(* net.TCPAddr)
	
	gn.Dht = dht
	gn.Pid = New_GoActorsNamingPid( ip, addr.Port )
	
	return gn, nil
}

func ( gn *GoActorsNaming ) Resolve( name * goActorsCommon.NameWrap, result * goActorsCommon.PidWrap ) os.Error {
	key := KademliaDht.GenerateKey( name.RealName )
	value, error := gn.Dht.Get( key )
	if error != nil {
		return error
	}
	decoder := gob.NewDecoder( bytes.NewBuffer( value ) )
	pid := goActorsCommon.New_GoActorPid( goActorsCommon.New_GoInstancePid( "", 0 ), 0 )
	error =  decoder.Decode( pid )
	if error != nil {
		return error
	}
	
	result.RealPid = pid
	
	return nil
}

func ( gn *GoActorsNaming ) Register ( args * goActorsCommon.RegisterArgsWrap, res * bool ) os.Error {
	key := KademliaDht.GenerateKey( args.RealName )
	bs := bytes.NewBuffer( make( []byte, 0, 0 ) )
	encoder := gob.NewEncoder( bs )
	error := encoder.Encode( args.RealPid )
	if error != nil {
		( *res ) = false
		return error
	}
	gn.Dht.Put( key, bs.Bytes() )
	( *res ) = true
	return nil
}

type GoActorsNamingPid struct {
	Ip		string
	Port	int
}

func New_GoActorsNamingPid( ip string, port int ) *GoActorsNamingPid {
	return &GoActorsNamingPid{ ip, port }
}

func ( gnp *GoActorsNamingPid )Register( name string, pid goActorsCommon.Pid ) ( res bool, error os.Error ) {
	client, error := rpc.Dial( "tcp", gnp.Ip + ":" + strconv.Itoa( gnp.Port ) )
	defer client.Close()
	if error != nil {
		return false, error
	}

	var callRes bool
	error = client.Call( "GoActorsNaming.Register", goActorsCommon.New_RegisterArgsWrap( name, pid ), &callRes )
	if error != nil {
		return false, error
	}
	
	return callRes, nil
}

func ( gnp *GoActorsNamingPid )Resolve( name string ) ( res goActorsCommon.Pid, error os.Error ) {
	client, error := rpc.Dial( "tcp", gnp.Ip + ":" + strconv.Itoa( gnp.Port ) )
	defer client.Close()
	if error != nil {
		return nil, error
	}

	callRes := goActorsCommon.New_PidWrap ( nil )
	error = client.Call( "GoActorsNaming.Resolve", goActorsCommon.New_NameWrap ( name ), callRes )
	if error != nil {
		return nil, error
	}
	
	return callRes.RealPid, nil
}

